﻿using System;
using System.Collections.Generic;
using System.Collections.Specialized;
//
using System.Linq;

using System.Windows;
using Caliburn.Micro;


namespace DailyFx.UI
{
	public class CollectionSyncer<T, U> : IWeakEventListener
		where T : class
		where U : class
	{

		private Func<T, U> mCreateFunction;
		private IObservableCollection<U> mSyncCollection2;


		public CollectionSyncer( ICollection<T> t, Func<T, U> createFunc)
		{
			mCreateFunction = createFunc ;

			INotifyCollectionChanged collection = t as INotifyCollectionChanged;
			if (collection == null)
			{
				throw new ArgumentException("The ICollection<T> must be INotifyCollectionChanged");
			}
			CollectionChangedEventManager.AddListener( collection, this);
			
			mSyncCollection2 = new BindableCollection<U>();
			t.Apply(item => mSyncCollection2.Add(mCreateFunction(item)));
		}

		public IObservableCollection<U> SyncCollection2
		{
			get
			{
				return mSyncCollection2;
			}
		}


		public bool ReceiveWeakEvent( Type managerType, object sender, EventArgs e)
		{
			if (managerType == typeof(CollectionChangedEventManager))
			{
				NotifyCollectionChangedEventArgs arg = e as NotifyCollectionChangedEventArgs;
				if (arg != null)
				{
					switch (arg.Action)
					{
						case NotifyCollectionChangedAction.Add:
							if (arg.NewItems != null)
							{
								for (int itemIndex = 0; itemIndex < arg.NewItems.Count; itemIndex++)
								{
									mSyncCollection2.Insert(arg.NewStartingIndex +
									  itemIndex, mCreateFunction(arg.NewItems[itemIndex] as T));
								}
								break;
							}
							break;
						case NotifyCollectionChangedAction.Move:
							mSyncCollection2.RemoveAt(arg.OldStartingIndex);
							mSyncCollection2.Insert(arg.NewStartingIndex, mCreateFunction(arg.NewItems[0] as T));
							break;
						case NotifyCollectionChangedAction.Remove:
							if (arg.OldItems != null && mSyncCollection2.Count > 0)
							{
								for (int itemIndex = 0; itemIndex < arg.OldItems.Count; itemIndex++)
								{
									mSyncCollection2.RemoveAt(arg.OldStartingIndex);
								}
							}
							break;
						case NotifyCollectionChangedAction.Replace:
							if (arg.NewItems == null)
							{
								for (int itemIndex = 0; itemIndex < arg.NewItems.Count; itemIndex++)
								{
									mSyncCollection2[arg.NewStartingIndex + itemIndex] = mCreateFunction(arg.NewItems[itemIndex] as T);
								}
							}
							break;
						case NotifyCollectionChangedAction.Reset:
							mSyncCollection2.Clear();
							break;
						default:
							break;
					}
				}
				return true;
			}
			return false;
		}

	}

	public class CollectionMerger<T> : IWeakEventListener where T : class
	{

		private readonly WeakReference mSource;
		private readonly int mIndex;


		public CollectionMerger( IList<T> source, ICollection<T> target, int index = -1)
		{
			if (source == null || target == null)
			{
				throw new ArgumentNullException("The collections of CollectionMerge should not be null");
			}
			if (index < 0)
			{
				mIndex = source.Count;
				target.Apply(item => source.Add(item));
			}
			else
			{
				mIndex = index;
				target.Apply(item =>
				{
					source.Insert(index, item);
					index++;
				});
			}

			INotifyCollectionChanged collection = target as INotifyCollectionChanged;
			if (collection != null)
			{
				mSource = new WeakReference(source);
				CollectionChangedEventManager.AddListener(collection, this);
			}
		}

		private IList<T> Source
		{
			get
			{
				if ( mSource.IsAlive )
				{
					return mSource.Target as IList<T>;
				}
				else
				{
					return default(IList<T>);
				}
			}
		}


		private int FindIndex( IList<T> source, IList<T> target)
		{
			if (target == null || target.Count == 0)
			{
				return mIndex;
			}

			var contains = source.Intersect(target);
			if (contains == null || contains.Count() == 0)
			{
				return mIndex;
			}

			return source.IndexOf(contains.First());
		}

		public bool ReceiveWeakEvent( Type managerType, object sender, EventArgs e)
		{
			if (managerType == typeof(CollectionChangedEventManager))
			{
				NotifyCollectionChangedEventArgs arg = e as NotifyCollectionChangedEventArgs;
				if (arg != null)
				{
					int startIndex = FindIndex(Source, sender as IList<T>);

					switch (arg.Action)
					{
						case NotifyCollectionChangedAction.Add:
							if (arg.NewItems != null)
							{
								for (int itemIndex = 0; itemIndex < arg.NewItems.Count; itemIndex++)
								{
									Source.Insert(startIndex + itemIndex, arg.NewItems[itemIndex] as T);
								}
								break;
							}
							break;
						case NotifyCollectionChangedAction.Move:
							Source.RemoveAt(startIndex + arg.OldStartingIndex);
							Source.Insert(startIndex + arg.NewStartingIndex, arg.NewItems[0] as T);
							break;
						case NotifyCollectionChangedAction.Remove:
						case NotifyCollectionChangedAction.Reset:
							if (arg.OldItems != null)
							{
								for (int itemIndex = 0; itemIndex < arg.OldItems.Count; itemIndex++)
								{
									Source.Remove(arg.OldItems[itemIndex] as T);
								}
							}
							break;
						case NotifyCollectionChangedAction.Replace:
							if (arg.NewItems == null)
							{
								for (int itemIndex = 0; itemIndex < arg.NewItems.Count; itemIndex++)
								{
									Source[startIndex + arg.NewStartingIndex + itemIndex] = arg.NewItems[itemIndex] as T;
								}
							}
							break;
						default:
							break;
					}
				}
				return true;
			}
			return false;
		}

	}
}
